#include "gtest/gtest.h"

#include "ObjectTest.h"
#include "helper/ScopedMemPtr.h"
#include "helper/object/RawType.h"

using namespace helper;
using namespace object;
class ObjectCopyTest : public ObjectTest {
public:
    template<typename T, typename E, typename Creater, typename Asserter>
    void testRawType(const E & expect, const Asserter & asserter, const Creater & creater){
        ScopedMemPtr<T> raw = creater(expect);
        ScopedMemPtr<Object> objNew = raw->copy();
        ASSERT_TRUE(asserter(objNew.get()));
        ASSERT_EQ(*raw, *static_cast<T*>(objNew.get()));
    }
    template<typename T, typename E, typename Creater>
    void testCloneRawType(const E & expect, const Creater & creater){
        ScopedMemPtr<T> raw = creater(expect);
        ScopedMemPtr<T> rawNew = raw->template clone<T>();
        ASSERT_EQ(*raw, *rawNew);
    }
};

TEST_F(ObjectCopyTest, testRawTypesCopy){
    testRawType<Byte>('c', [](Object * obj){ return obj->isByte();}
        , [this](const char & expect) { 
            return this->getObjectFactory()->createByte(expect);
    });
    testRawType<Integer>(22ll, [](Object * obj){ return obj->isInteger();}
        , [this](const long long & expect) { 
            return this->getObjectFactory()->createInteger(expect);
    });
    testRawType<Double>(33.0, [](Object * obj){ return obj->isDouble();}
        , [this](const double & expect) { 
            return this->getObjectFactory()->createDouble(expect);
    });
    testRawType<String>("hello", [](Object * obj){ return obj->isString();}
        , [this](const std::string & expect) { 
            return this->getObjectFactory()->createString(expect);
    });
    testRawType<Pointer>((const void*)1, [](Object * obj){ return obj->isPointer();}
        , [this](const void * expect) { 
            return this->getObjectFactory()->createPointer(expect);
    });
}
TEST_F(ObjectCopyTest, testCopyObject){
    ScopedMemPtr<Object> obj = getObjectFactory()->createByte('c');
    ScopedMemPtr<Object> objNew = obj->copy();
    ASSERT_EQ(*obj, *objNew);
}
TEST_F(ObjectCopyTest, testCopyArray){
    ScopedMemPtr<Array> obj = getObjectFactory()->createArray();
    ScopedMemPtr<Object> objNew = obj->copy();
    ASSERT_TRUE(objNew->isArray());
    ASSERT_EQ(*obj, *static_cast<Array*>(objNew.get()));
}
TEST_F(ObjectCopyTest, testCopyHash){
    ScopedMemPtr<Hash> obj = getObjectFactory()->createHash();
    ScopedMemPtr<Object> objNew = obj->copy();
    ASSERT_TRUE(objNew->isHash());
    ASSERT_EQ(*obj, *static_cast<Hash*>(objNew.get()));
}


TEST_F(ObjectCopyTest, testCloneObject){
    ScopedMemPtr<Object> obj = getObjectFactory()->createByte('c');
    ScopedMemPtr<Object> objNew = obj->clone<Object>();
    ASSERT_EQ(*obj, *objNew);
}

TEST_F(ObjectCopyTest, testCloneRawTypes){
    testCloneRawType<Byte>('c', [this](const char & expect){
        return this->getObjectFactory()->createByte(expect);
    });
    testCloneRawType<Integer>(2ll, [this](const long long & expect){
        return this->getObjectFactory()->createInteger(expect);
    });
    testCloneRawType<Double>(3.0, [this](const double & expect){
        return this->getObjectFactory()->createDouble(expect);
    });
    testCloneRawType<String>("string", [this](const std::string & expect){
        return this->getObjectFactory()->createString(expect);
    });
    testCloneRawType<Pointer>((const void *)1, [this](const void * expect){
        return this->getObjectFactory()->createPointer(expect);
    });
}
TEST_F(ObjectCopyTest, testCloneArray){
    ScopedMemPtr<Array> raw = getObjectFactory()->createArray();
    ScopedMemPtr<Array> rawNew = raw->clone<Array>();
    ASSERT_EQ(*raw, *rawNew);
}
TEST_F(ObjectCopyTest, testCloneHash){
    ScopedMemPtr<Hash> raw = getObjectFactory()->createHash();
    ScopedMemPtr<Hash> rawNew = raw->clone<Hash>();
    ASSERT_EQ(*raw, *rawNew);
}
